package org.example.protocol.bilibili.packet;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import lombok.Getter;

//@Data
//@Accessors(chain = true)
@Getter
public class BiliBiliDataPacket {
    private static final long uint32_mask = 0xFFFFFFFFL;
    private static final int uint16_mask = 0xFFFF;
    private Long packetSize;
    private Integer headerSize;
    private ProtocolVersion protocolVersion;
    private PacketType packetType;
    private Long sequence;
    private String content;
    private byte[] packetBytes;

    public BiliBiliDataPacket(ProtocolVersion protocolVersion,
            PacketType packetType,
            String content,
            Long sequence) {
        this.protocolVersion = protocolVersion;
        this.packetType = packetType;
        this.content = content;
        this.sequence = sequence;
    }

    public byte[] buildPacket() {
        long headerSize = 16L;
        byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
        long contentSize = contentBytes.length;
        long totalSize = headerSize + contentSize;
        ByteBuffer buffer = ByteBuffer.allocate((int) totalSize);
        buffer.putInt((int) (totalSize & uint32_mask)); // 总长度
        buffer.putShort((short) (headerSize & uint16_mask));
        buffer.putShort((short) (protocolVersion.getCode() & uint16_mask));
        buffer.putInt((int) (packetType.getCode() & uint16_mask));
        buffer.putInt((int) (sequence & uint32_mask));
        buffer.put(contentBytes);
        this.packetBytes = buffer.array();
        return packetBytes;
    }

    public static List<BiliBiliDataPacket> parsePackets(ByteBuffer buffer) throws Exception {
        List<BiliBiliDataPacket> allPackets = new ArrayList<>();
        while (buffer.hasRemaining()) {
            BiliBiliDataPacket.parsePacket(buffer, allPackets);
        }
        return allPackets;
    }

    /**
     * 解析数据包
     * 
     * @param buffer
     * @return
     * @throws Exception
     */
    public static void parsePacket(ByteBuffer buffer, List<BiliBiliDataPacket> allPackets)
            throws Exception {
        // 读取固定头部字段（16字节）
        long totalSize = buffer.getInt() & uint32_mask; // 总长度
        int headerSize = buffer.getShort() & uint16_mask; // 头部长度
        int protocolVersionCode = buffer.getShort() & uint16_mask; // 协议版本
        long packetTypeCode = buffer.getInt() & uint32_mask; // 包类型
        long sequence = buffer.getInt() & uint32_mask; // 序列号

        // 验证数据有效性
        if (totalSize < 16) {
            throw new IllegalArgumentException("Invalid packet size: " + totalSize);
        }

        // 包协议版本
        ProtocolVersion protocolVersion = ProtocolVersion.fromCode(protocolVersionCode);
        // 包类型
        PacketType packetType = PacketType.fromCode(packetTypeCode);
        BiliBiliDataPacket dataPacket;
        byte[] content = new byte[0];
        switch (packetType) {
            case HEARTBEAT_RESPONSE:

                // 心跳包响应
                long popularityValue = buffer.getInt() & uint32_mask;
                // 计算内容长度并读取内容
                int contentSize = buffer.remaining();
                content = new byte[contentSize];
                buffer.get(content); // 读取内容部分
                dataPacket = new BiliBiliDataPacket(
                        protocolVersion,
                        packetType,
                        new String(content, StandardCharsets.UTF_8),
                        sequence);
                allPackets.add(dataPacket);
                return;
            case AUTHENTICATION_RESPONSE:
                // 认证包回复
                // 计算内容长度并读取内容
                contentSize = (int) (totalSize - headerSize);
                content = new byte[contentSize];
                buffer.get(content); // 读取内容部分
                dataPacket = new BiliBiliDataPacket(
                        protocolVersion,
                        packetType,
                        new String(content, StandardCharsets.UTF_8),
                        sequence);
                allPackets.add(dataPacket);
                return;
            case COMMAND:
                // 命令包
                if (protocolVersion == ProtocolVersion.BROTLI_COMPRESSED_NORMAL) {
                    // 正文采用brotli算法压缩，需要先解压
                    int length = (int) (totalSize - headerSize);
                    byte[] compressData = new byte[length];
                    buffer.get(compressData);
                    content = BrotliComprssUtils.decompress(compressData);
                    ByteBuffer contentBuffer = ByteBuffer.wrap(content);
                    parsePacket(contentBuffer, allPackets);
                } else if (protocolVersion == ProtocolVersion.ZLIB_COMPRESSED_NORMAL) {
                    // 正文采用zlib压缩
                    int length = (int) (totalSize - headerSize);
                    byte[] compressData = new byte[length];
                    buffer.get(compressData);
                    content = ZlibCompressUtils.decompress(compressData);
                    ByteBuffer contentBuffer = ByteBuffer.wrap(content);
                    parsePacket(contentBuffer, allPackets);
                } else {
                    // 计算内容长度并读取内容
                    contentSize = (int) (totalSize - headerSize);
                    content = new byte[contentSize];
                    buffer.get(content); // 读取内容部分
                    dataPacket = new BiliBiliDataPacket(
                            protocolVersion,
                            packetType,
                            new String(content, StandardCharsets.UTF_8),
                            sequence);
                    allPackets.add(dataPacket);
                    if (buffer.hasRemaining()) {
                        parsePacket(buffer, allPackets);
                    }
                    return;
                }
                break;
            default:
                break;
        }
    }

}
